use std::{path::PathBuf, process::Command, vec};

// ref from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
/* 32-bit ELF base types. */
type Elf32Addr = u32;
type Elf32Half = u16;
type Elf32Off = u32;
type Elf32Sword = i32;
type Elf32Word = u32;

/* 64-bit ELF base types. */
type Elf64Addr = u64;
type Elf64Half = u16;
type Elf64SHalf = i16;
type Elf64Off = u64;
type Elf64Sword = i32;
type Elf64Word = u32;
type Elf64Xword = u64;
type Elf64Sxword = i64;

const EI_NIDENT: usize = 16;
const EI_MAG0: usize = 0; /* e_ident[] indexes */
const EI_MAG1: usize = 1;
const EI_MAG2: usize = 2;
const EI_MAG3: usize = 3;
const EI_CLASS: usize = 4;
const EI_DATA: usize = 5;
const EI_VERSION: usize = 6;
const EI_OSABI: usize = 7;
const EI_PAD: usize = 8;

const ELFMAG0: u8 = 0x7f; /* EI_MAG */
const ELFMAG1: u8 = b'E';
const ELFMAG2: u8 = b'L';
const ELFMAG3: u8 = b'F';
const SELFMAG: usize = 4;
const ELFMAG: [u8; SELFMAG] = [ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3];

struct ElfEhdr<Half, Word, Addr, Off> {
    e_ident: [u8; EI_NIDENT], // ELF "magic number"
    e_type: Half,
    e_machine: Half,
    e_version: Word,
    e_entry: Addr, // Entry point virtual address
    e_phoff: Off,  // Program header table file offset
    e_shoff: Off,  // Section header table file offset
    e_flags: Word,
    e_ehsize: Half,
    e_phentsize: Half,
    e_phnum: Half,
    e_shentsize: Half,
    e_shnum: Half,
    e_shstrndx: Half,
}

type Elf64Ehdr = ElfEhdr<Elf64Half, Elf64Word, Elf64Addr, Elf64Off>;
type Elf32Ehdr = ElfEhdr<Elf32Half, Elf32Word, Elf32Addr, Elf32Off>;

// Elf32Phdr 和 Elf64Phdr 中 flag 的顺序不同
struct Elf32Phdr {
    p_type: Elf32Word,
    p_offset: Elf32Off,
    p_vaddr: Elf32Addr,
    p_paddr: Elf32Addr,
    p_filesz: Elf32Word,
    p_memsz: Elf32Word,
    p_flags: Elf32Word,
    p_align: Elf32Word,
}

struct Elf64Phdr {
    p_type: Elf64Word,
    p_flags: Elf64Word,
    p_offset: Elf64Off,   /* Segment file offset */
    p_vaddr: Elf64Addr,   /* Segment virtual address */
    p_paddr: Elf64Addr,   /* Segment physical address */
    p_filesz: Elf64Xword, /* Segment size in file */
    p_memsz: Elf64Xword,  /* Segment size in memory */
    p_align: Elf64Xword,  /* Segment alignment, file & memory */
}

pub enum SymbolType {
    NoType,
    Func,
    Section,
    File,
    Object,
}

pub enum BindType {
    Local,
    Global,
}

pub enum VisType {
    Default,
}

pub struct Symbol<Word> {
    pub value: Word,
    pub size: Word,
    pub sym_type: SymbolType,
    pub bind: BindType,
    pub vis: VisType,
    pub ndx: String, // todo: 搞明白含义
    pub name: Option<String>,
}

pub struct Elf {
    file: PathBuf,
}

impl Elf {
    pub fn new(path: impl Into<PathBuf>) -> Elf {
        Elf { file: path.into() }
    }

    /// 之后考虑不同平台，目前仅计划支持 Riscv64
    // 主要在 windows 上测试，用 readelf 是不是不太合适，考虑自己写吧，这里先暂停
    pub fn symtab_riscv64(&self) -> Option<Vec<Symbol<u64>>> {
        if let Ok(output) = Command::new("riscv64-linux-gnu-readelf")
            .arg("-a")
            .arg(self.file.as_os_str())
            .output()
        {
            let output = String::from_utf8(output.stdout).unwrap(); // 这种应该不会出错吧？
            Some(vec![])
        } else {
            eprintln!("can not find riscv64-linux-gnu-readelf");
            None
        }
    }
}
